package edu.siu.math.egut.io;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.List;
import java.util.Scanner;

import edu.siu.math.egut.util.ExceptionalGroup;



/**
 * Collection of static algorithms, related to I/O functions which are not 
 * part of the Egut interpreter.
 * 
 * @author josephhundley
 *
 */
final public class Algorithms {
    
    /**
     * Some classes for interacting with the user in a 
     * fault tolerant way.
     * 
     */
    
    private Algorithms(){
	
    }
    
    /**
     * @param scanner
     * @return
     */
    public static String getFileName( Scanner scanner){
	String filename =  scanner.nextLine();
	File file = new File(filename);
	
	// handle the relatively likely occurrences of mistyped or forgotten filenames.
	while (!file.exists()){
	    System.out.println("I could not find a file called \""+filename+".\"  Please try again or type \"quit\" to quit." +
	    		"(If you think your file is called \"quit,\" I'm sorry, but I'm afraid that will be a problem.)");
	    filename = scanner.nextLine();
	    if(filename.trim().equals("quit"))
		System.exit(1);
	    else
		file = new File(filename);
	    
	    
	}// end while 
	
	return filename;
    }
    
    /**
     * @param scanner
     * @param g
     * @return
     */
    public static int[] getRoot( Scanner scanner, ExceptionalGroup g){
	int input = scanner.nextInt();
	scanner.nextLine();
	boolean done = false;
	while( !done){
	int[] root = rootFromInt(input, g.getRank());
	if( g.isRoot(root))
	    return root;	
	else 
	    System.out.println("Does not match a root.  Please try again.\n");
	}
	return null;
    }
    
    /**
     * @param scanner
     * @param name
     * @param pieces
     * @return
     */
    public static String getPieceName(Scanner scanner, String name , List<String> pieces){
	boolean gotIt = false;
	String desired = name;
	while( !gotIt ){
	    String suffix = scanner.next();
	    if(suffix.equalsIgnoreCase("null"))
		desired = name;
	    else if( suffix.equalsIgnoreCase("quit"))
		return null;
	    else 
		desired = name + suffix;
	    if(pieces.contains(desired))
		gotIt = true;
	    System.out.println("That does not appear to be a piece from the list.  Here is the list again:\n"+pieces+"\n"
		   +"What you type will be appended to the following prefix:  "+name+"Please try again.\n" );
	    
	}
	   
	return desired;
    }
  
    /**
     * Some classes designed to allow egut to acquire lists of roots by reading LiE monfiles.
     */
    
    /**
     * Convert a file into one big String.
     * @param filename  the name of the file to be read
     * @return a String containing the contents of the file
     * @throws FileNotFoundException if the file is not found.
     * 
     */
    public static String fileToString( String filename) throws FileNotFoundException{
	final Scanner input;
	try {
	    input = new Scanner(new File(filename));
	} catch (FileNotFoundException fnf) {
	    System.out.printf("file not found.");
	    throw fnf;

	}// end catch
	
	
	// concatenate all file contents into a big string
	StringBuffer sb = new StringBuffer();

	while (input.hasNext()) {
	    sb.append(input.next().trim());
	}// end while
	String s = sb.toString();
	return s;
    }
    
   /**
    * Extracts a matrix -- formatted as it would be in LiE output, from a String.
    * The string may contain other things before and/or after the LiE matrix.  If the 
    * string contains more than one LiE matrix, this method should locate and 
    * return the first. 
 * @param s a String, which should contain a matrix, formatted as a bracketed list of bracketed lists, as in LiE output.
 * @return an int[][] version of the first such matrix appearing in the String s.
 * @throws Exception if (1) the file does not have the form ... [[ ... ]] ... (i.e., no "matrix" is found)
 *                      (2) the matrix has zero rows -- i.e., is [[]].
 *                      (3) the rows of the matrix are of unequal lengths, or 
 *                      (4) any of the rows is length zero (i.e., []).
 *                     TODO use custom exceptions instead of just Exception class.
 *                     Do something to enable being able to tell which of the four potential problems occurred.
 */
public static int[][] readLiEMatrix( String s ) throws Exception {
	
	int[][] m = new int[1][1];
	
	
	/*
	 * This program will look for a LiE output formatted matrix in the
	 * file and ignore everything else.
	 */

	// String should have format ...[[...]]....
	int beginningOfMatrix = s.indexOf("[[") + 2;
	int endOfMatrix = s.indexOf("]]");
	try {

	    if (beginningOfMatrix > -1
		    && endOfMatrix > beginningOfMatrix) {
		String firstMatrix = s.substring(
			beginningOfMatrix, endOfMatrix);
		String[] rows = firstMatrix.split("\\],\\[");
		int numRows = rows.length;
		    
		if (numRows > 0) {
		    int numCols = rows[0].split(",").length;
		    if( numCols > 0 ){
			 m = new int[numRows][numCols];
			for( int i = 0; i < numRows; i++)
			{
			    String[] row = rows[i].split(",");
			    if(row.length !=numCols )
				throw new Exception();
			    for( int j = 0; j < numCols; j++)
				m[i][j] = Integer.parseInt(row[j]);
			}
			
		    }else 
			throw new Exception("numCols <=0");

		    
		}// end if numRows > 0
		else
		    throw new Exception("numRows <= 0");

	    }// end if beginningOfFirstMatrix >-1 ...
	    else
		throw new Exception();
	}/* end try block */catch (Exception e) {
	    System.out.println("reading failed.");
	    e.printStackTrace();

	}// end catch
	
	return m;
	
	
    }// end method readLiEMatrix ( String ) 

/**
 * Extracts a matrix -- formatted as it would be in LiE output, from a String.
 * The matrix must occur in the substring beginning at the integer "startingPoint."
 * The string may contain other things before and/or after the LiE matrix.  If the 
 * string contains more than one LiE matrix, this method should locate and 
 * return the first which begins at or after startingPoint.
 * 
*  
* @param s  a String, which should contain a matrix, formatted as a bracketed list of bracketed lists, as in LiE output, 
* which begins at or after startingPoint
 *@param startingPoint  an integer, which indicates where to start looking for the matrix.
 *
* @return an int[][] version of the first such matrix appearing in the String s.
* @throws Exception if (1) the file does not have the form ... [[ ... ]] ... (i.e., no "matrix" is found)
*                      (2) the matrix has zero rows -- i.e., is [[]].
*                      (3) the rows of the matrix are of unequal lengths, or 
*                      (4) any of the rows is length zero (i.e., []).
*                     TODO use custom exceptions instead of just Exception class.
*                     Do something to enable being able to tell which of the four potential problems occurred.
*/
   public static int[][] readLiEMatrix( String s, int startingPoint ) throws Exception {
	
	int[][] m = new int[1][1];
	
	
	/*
	 * This program will look for a LiE output formatted matrix in the
	 * file and ignore everything else.
	 */

	// file should have format ...[[...]]....
	int beginningOfMatrix = s.indexOf("[[", startingPoint) + 2;
	int endOfMatrix = s.indexOf("]]", startingPoint);
	try {

	    if (beginningOfMatrix > -1
		    && endOfMatrix > beginningOfMatrix) {
		String firstMatrix = s.substring(
			beginningOfMatrix, endOfMatrix);
		String[] rows = firstMatrix.split("\\],\\[");
		int numRows = rows.length;
		    
		if (numRows > 0) {
		    int numCols = rows[0].split(",").length;
		    if( numCols > 0 ){
			 m = new int[numRows][numCols];
			for( int i = 0; i < numRows; i++)
			{
			    String[] row = rows[i].split(",");
			    for( int j = 0; j < numCols; j++)
				m[i][j] = Integer.parseInt(row[j]);
			}
			
		    }else 
			throw new Exception("numCols <= 0");

		    
		}// end if numRows > 0
		else
		    throw new Exception("numRows <= 0");

	    }// end if beginningOfFirstMatrix >-1 ...
	    else
		throw new Exception("no Matrix found");
	}/* end try block */catch (Exception e) {
	    System.out.println("reading failed.");
	    e.printStackTrace();
	    throw e;

	}// end catch
	
	return m;
	
	
   }// end method readLiEMatrix ( String, int) 

/**
 * 
 */
public final static int LINELENGTH = 80;

/**
 * Parse an integer into an int[] of the given size.
 * (This is the most convenient way to input roots of exceptional groups by hand.
 * We are exploiting the fact that when a root is written as a linear combination of the 
 * simple roots, each of the coefficients is a single-digit number.)
 * @param input the int to be parsed
 * @param rank the size for the parsed int[]
 * @return the parsed an int[] of size rank which contains the last rank digits of input
 * (Examples:  11 and 2 gives [1,1]; 321 and 3 gives [1,1]; 11 and 8 gives [0,0,0,0,0,0,1,1].)
 */
public static int[] rootFromInt(int input, final int rank){
    int[] root = new int[rank];
    for( int i = 1; i <= rank; i++){
        root[ rank-i] = input % 10;
        input = input/10;
    }
    return root;
    
}
}
